/*
 ------------------------------------------------------------------------------
 Copyright (C) 2006-2007 Team Blur.

 This file is part of the Quantum Engine source code.

 The Quantum Engine source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The Quantum Engine source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the Quantum Engine source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


#include "sg_local.h"


/*
 ==============================================================================

 COMMAND EXECUTION

 ==============================================================================
*/

/*
 ==================
 SG_CmdNoclip
 ==================
*/
void SG_CmdNoclip (client_t *client){

	entity_t	*entity;
	const char	*message;

	entity = level.entities[client->persistent.clientNum];

	entity->flags ^= EF_NOCLIP;

	if (entity->flags & EF_NOCLIP)
		message = "noclip ON";
	else
		message = "noclip OFF";

	sgi->AddReliableCommand(client->persistent.clientNum, "print %s\n", message);
}


/*
 ==============================================================================

 PUBLIC INTERFACE

 ==============================================================================
*/


/*
 ==================
 SG_ClientConnect
 ==================
*/
const char *SG_ClientConnect (int clientNum, bool firstTime, const char *ip, char *userInfo){

	client_t	*client;

	if (clientNum < 0 || clientNum >= MAX_CLIENTS)
		odSystem->Error(false, "SG_ClientConnect: bad clientNum (%i)", clientNum);

	// Allocate a new client
	level.clients[clientNum] = client = (client_t *)odMemory->ClearedAlloc(sizeof(client_t), TAG_SERVER);

	client->persistent.connected = false;
	client->persistent.clientNum = clientNum;

	// Check for local client
	if (!Str_ICompare(ip, "localhost"))
		client->persistent.localClient = true;

	// Get and distribute relevant parameters
	SG_ClientUserInfoChanged(clientNum, userInfo);

	// Don't do the "xxx connected" messages if they were carried over from
	// the previous level
	if (firstTime)
		sgi->BroadcastReliableCommand("print %s" S_COLOR_DEFAULT " connected\n", client->persistent.name);

	return NULL;
}

/*
 ==================
 SG_ClientDisconnect
 ==================
*/
void SG_ClientDisconnect (int clientNum){

	client_t	*client;

	if (clientNum < 0 || clientNum >= MAX_CLIENTS){
		odSystem->Error(false, "SG_ClientDisconnect: bad clientNum (%i)", clientNum);
		return;
	}

	client = level.clients[clientNum];
	if (!client)
		return;

	// Free the client
	odMemory->Free(level.clients[clientNum]);
	level.clients[clientNum] = NULL;

	// Free the entity if needed
	if (level.entities[clientNum])
		SG_FreeEntity(level.entities[clientNum]);
}

/*
 ==================
 SG_ClientUserInfoChanged
 ==================
*/
void SG_ClientUserInfoChanged (int clientNum, char *userInfo){

	client_t	*client;
	const char	*infoString;
	char		*ch;
	const char	*name;

	if (clientNum < 0 || clientNum >= MAX_CLIENTS){
		odSystem->Error(false, "SG_ClientUserInfoChanged: bad clientNum (%i)", clientNum);
		return;
	}

	client = level.clients[clientNum];
	if (!client)
		return;

	// Parse all the information we need
	infoString = userInfo;

	while (1){
		ch = Str_FindChar(infoString, '\\');
		if (!ch)
			break;

		*ch++ = 0;

		// Check the name/value pair
		odCmdSystem->TokenizeString(infoString);

		if (!Str_ICompare(odCmdSystem->Argv(0), "cl_playerName")){
			name = odCmdSystem->Args(1, -1, false);

			if (client->persistent.connected){
				if (Str_Compare(client->persistent.name, name))
					sgi->BroadcastReliableCommand("print %s" S_COLOR_DEFAULT " renamed to %s\n", client->persistent.name, name);
			}

			Str_Copy(client->persistent.name, name, sizeof(client->persistent.name));
		}

		// Advance
		infoString = (const char *)ch;

		*--ch = '\\';
	}

	// Update config string with a subset of the user info so other clients can
	// print scoreboards, display models, and play custom sounds
	infoString = Str_VarArgs("cl_playerName %s\\", client->persistent.name);

	SG_SetConfigString(CS_CLIENTS + clientNum, infoString);
}

/*
 ==================
 SG_ClientBegin
 ==================
*/
void SG_ClientBegin (int clientNum){
	client_t	*client;
	entity_t	*entity;

	if (clientNum < 0 || clientNum >= MAX_CLIENTS){
		odSystem->Error(false, "SG_ClientBegin: bad clientNum (%i)", clientNum);
		return;
	}

	client = level.clients[clientNum];
	if (!client)
		return;

	client->persistent.connected = true;

	// Set the callbacks to test the world
	client->physics.SetContentsFunction(SG_Contents);
	client->physics.SetTraceFunction(SG_Trace);

	// Allocate an entity
	entity = SG_AllocEntitySlot(client->persistent.clientNum);

	entity->className = "player";
	entity->name = client->persistent.name;

	entity->client = client;

	entity->contents = CONTENT_BODY;
//	entity->bounds.Set(-10.0f, 10.0f);
	entity->bounds.Set(-16.0f, -16.0f, 0.0f, 16.0f, 16.0f, 80.0f);

	entity->flags = EF_NOCLIP;
	entity->health = 100;
}

/*
 ==================
 SG_ClientCommand
 ==================
*/
void SG_ClientCommand (int clientNum){

	client_t	*client;
	const char	*name;

	if (clientNum < 0 || clientNum >= MAX_CLIENTS){
		odSystem->Error(false, "SG_ClientCommand: bad clientNum (%i)", clientNum);
		return;
	}

	client = level.clients[clientNum];
	if (!client)
		return;

	if (!client->persistent.connected)
		return;		// Not fully in game yet

	name = odCmdSystem->Argv(0);

	if (!Str_ICompare(name, "noclip"))
		SG_CmdNoclip(client);
	else
		sgi->AddReliableCommand(clientNum, "print Unknown command '%s'\n", name);
}

/*
 ==================
 SG_ClientThink
 ==================
*/
void SG_ClientThink (int clientNum, const userCmd_t *userCmd){

	client_t	*client;
	entity_t	*entity;
	userCmd_t	newUserCmd;
	int			msec;

	if (clientNum < 0 || clientNum >= MAX_CLIENTS){
		odSystem->Error(false, "SG_ClientThink: bad clientNum (%i)", clientNum);
		return;
	}

	client = level.clients[clientNum];
	if (!client)
		return;

	if (!client->persistent.connected)
		return;		// Not fully in game yet

	entity = level.entities[clientNum];

	newUserCmd = *userCmd;

	if (newUserCmd.time > level.time + 200)
		newUserCmd.time = level.time + 200;
	if (newUserCmd.time < level.time - 1000)
		newUserCmd.time = level.time - 1000;

	msec = newUserCmd.time - client->state.userCmdTime;
	if (msec < 1)
		return;

	if (msec > 200)
		msec = 200;

//	newUserCmd.time = ((newUserCmd.time + pm_moveMsec->integerValue - 1) / pm_moveMsec->integerValue) * pm_moveMsec->integerValue;
	newUserCmd.time = ((newUserCmd.time + 8 - 1) / 8) * 8;

//	if (spectator){
//		state.pmType = PM_SPECTATOR;

//		state.speed = 400.0f;			// TODO: pm_spectatorSpeed->floatValue
//		state.sprintSpeedScale = 1.0f;	// TODO!!!
//	}
//	else if (entity->flags & EF_NOCLIP){
	if (entity->flags & EF_NOCLIP){
		client->state.pmType = PM_NOCLIP;

//		state.speed = 400.0f;			// TODO: pm_noclipSpeed->floatValue
		client->state.sprintSpeedScale = 1.0f;	// TODO!!!

		extern cvar_t *sg_noclipSpeed;

		client->state.speed = sg_noclipSpeed->floatValue;
	}
//	else if (entity->health <= 0){
//		state.pmType = PM_DEAD;

//		state.speed = 320.0f;			// TODO: pm_moveSpeed->floatValue
//		state.sprintSpeedScale = 1.0f;	// TODO!!!
//	}
	else {
		client->state.pmType = PM_NORMAL;

//		state.speed = 320.0f;			// TODO: pm_moveSpeed->floatValue
		client->state.sprintSpeedScale = 1.0f;	// TODO!!!

		extern cvar_t *sg_moveSpeed;

		client->state.speed = sg_moveSpeed->floatValue;
	}

	extern cvar_t *sg_gravity;

	client->state.gravity = sg_gravity->floatValue;

	// Move the player
	client->physics.SetMoveMsec(8);		// TODO: pm_moveMsec->integerValue

	if (client->state.pmType == PM_DEAD)
		client->physics.SetContentMask(MASK_DEADSOLID);
	else
		client->physics.SetContentMask(MASK_PLAYERSOLID);

	client->physics.SetUserCmd(&newUserCmd);

	client->physics.MovePlayer(&client->state);

	// Link the player entity
	entity->origin = client->state.origin;
	entity->angles = client->state.angles;
	entity->axis = client->state.angles.ToMat3();

	// TODO: get bounds from animated model, not physics!
	entity->bounds = client->physics.GetBounds();

	SG_LinkEntity(entity);
}

void SG_ClientWriteGameState (int clientNum, ByteBuffer *msg){
/*
	client_t	*client;
	int			startSize, endSize;
	int			index, count = 0;

	if (clientNum < 0 || clientNum >= MAX_CLIENTS){
		odSystem->Error(false, "SG_ClientWriteGameState: bad clientNum (%i)", clientNum);
		return;
	}

	client = level.clients[clientNum];
	if (!client)
		return;

	if (offset >= GS_START_CONFIG_STRINGS && offset <= GS_END_CONFIG_STRINGS){
		// Leave space for header
		startSize = msg->getBitsWritten();

		msg->WriteLong(0);
		msg->WriteLong(0);
		msg->WriteLong(0);

		// Write as many config strings as possible
		index = offset - GS_START_CONFIG_STRINGS;

		while (index < MAX_CONFIG_STRINGS){
			if (!level.configStrings[index]){
				index++;
				continue;
			}

			if (msg->getBytesWritten() + Str_Length(level.configStrings[index]) + 32 >= msg->getSize())
				break;

			msg->WriteShort(index);
			msg->WriteString(level.configStrings[index]);

			count++;
			index++;
		}

		if (index != MAX_CONFIG_STRINGS)
			index = GS_START_CONFIG_STRINGS + index;
		else
			index = GS_START_BASELINES;

		// Write the header
		endSize = msg->getBitsWritten();

		msg->SetWriteState(startSize);

		msg->WriteLong(offset);	// Start offset
		msg->WriteLong(count);		// Number of config strings
		msg->WriteLong(index);		// Next offset

		msg->SetWriteState(endSize);

		return;
	}

	if (offset >= GS_START_BASELINES && offset <= GS_END_BASELINES){
		// Leave space for header
		startSize = msg->getBitsWritten();

		msg->WriteLong(0);
		msg->WriteLong(0);
		msg->WriteLong(0);

		// Write as many baselines as possible
		index = offset - GS_START_BASELINES;

		while (index < MAX_ENTITIES){
			if (!level.entities[index]){
				index++;
				continue;
			}

			if (msg->getBytesWritten() + sizeof(entityState_t) + 32 >= msg->getSize())
				break;

			SG_WriteBaseline(level.entities[index], msg);

			count++;
			index++;
		}

		if (index != MAX_ENTITIES)
			index = GS_START_BASELINES + index;
		else
			index = -1;

		// Write the header
		endSize = msg->getBitsWritten();

		msg->SetWriteState(startSize);

		msg->WriteLong(offset);	// Start offset
		msg->WriteLong(count);		// Number of baselines
		msg->WriteLong(index);		// Next offset

		msg->SetWriteState(endSize);

		return;
	}

	// Invalid game state request
	msg->WriteLong(-1);
	msg->WriteLong(-1);
	msg->WriteLong(-1);
	*/
}

/*
 ==================
 SG_ClientWriteSnapshot
 ==================
*/
void SG_ClientWriteSnapshot (int clientNum, ByteBuffer *msg){

	client_t	*client;

	if (clientNum < 0 || clientNum >= MAX_CLIENTS){
		odSystem->Error(false, "SG_ClientWriteSnapshot: bad clientNum (%i)", clientNum);
		return;
	}

	client = level.clients[clientNum];
	if (!client)
		return;

	SG_WriteSnapshotToClient(client, msg);
}
